﻿using System;
using NUnit.Framework;
using MoneyWentWhere.SimpleTextFile;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using MoneyWentWhere.Test.MwwSimple;
using MoneyWentWhere.Utils;

namespace MoneyWentWhere.MwwSimpleTest
{
    public class FileTestData : ITestCaseData
    {
        public const String HTest = "===== Test =====";
        const String HBefore = "=== Before ===";
        const String HAfter = "=== After ===";

        const String FCode = "@Code";

       
        public String Comment;
        public readonly String Before;
        public readonly String ExpectedOutput;

        public FileTestData(String comment, String before, String after)
        {
            Comment = comment;
            Before = before;
            ExpectedOutput = after;

            // Extract stuff
            ExtractCommentInfo();           
        }

        private void ExtractCommentInfo()
        {
            String[] lines = Comment.Split('\r', '\n');

            if (lines.Length > 0) { _testName = lines[0].Trim(); }

            foreach (String line in lines)
            {
                int eqPos = line.IndexOf("=");
                if (eqPos == -1) { continue; }

                if (line.TrimStart().StartsWith(FCode))
                {
                    String methodName = line.Substring(eqPos + 1).Trim();

                    MethodInfo mi = typeof(FileTestCode).GetMethod(methodName, new Type[] { typeof(MwwDataFile) });
                    if (mi == null) { throw new ApplicationException("Test: " + Comment + " code not found: " + methodName); }
                    _codeMethod = mi;
                }

            }
        }

        // Extracted from comment
        String _testName;
        MethodInfo _codeMethod;

        /// <summary>
        /// Parse tests from a specific test set. 
        /// Set 'XXX' is in file MwwSimple.Test\tcXXX.txt
        /// </summary>
        /// <param name="testSetName"></param>
        public static IEnumerable<FileTestData> ParseTests(String testSetName)
        {
            String text = File.ReadAllText(Path.Combine(@"MwwSimple\tc", testSetName + ".txt"));

            // === Before
            String[] testReps = text.Split(new String[] { FileTestData.HTest }, StringSplitOptions.RemoveEmptyEntries);

            List<FileTestData> tests = new List<FileTestData>();
            for (int i=0; i<testReps.Length; i++)
            {
                FileTestData ft = FileTestData.Parse(testReps[i]);
                if (String.IsNullOrEmpty(ft.Comment.Trim())) { ft.Comment = "Test #" + (i+1); }
                tests.Add(ft);
            }

             

            return tests;
        }


        public static FileTestData Parse(String stringRep)
        {
            int index = 0;
            String[] parts = stringRep.Split( new String[] { HBefore, HAfter }, StringSplitOptions.RemoveEmptyEntries);

            //Assert.AreEqual(HTest, parts[index++]);
            String Comment = parts[index++].Trim(' ', '\t', '\r', '\n');

            //Assert.AreEqual(HBefore, parts[index++]);
            String Before = parts[index++].Trim(' ', '\t', '\r', '\n');

            //Assert.AreEqual(HAfter, parts[index++]);
            String After = parts[index++].Trim(' ', '\t', '\r', '\n');

            return new FileTestData(Comment, Before, After);
        }

        public String StringRep
        {
            get 
            {
                return
                    HTest + "\r\n" + Comment + "\r\n" +
                    HBefore + "\r\n" + Before + "\r\n" +
                    "Expected" + "\r\n" + ExpectedOutput + "\r\n";
            }
        }

        public void Run()
        {
            MwwDataFile df = new MwwDataFile();
            df.ReadString(Before);

            if (_codeMethod != null)
            {
                FileTestCode ftc = new FileTestCode();
                _codeMethod.Invoke(ftc, new object[] { df });
            }

            String output = df.WriteString();

            Compare(ExpectedOutput, output);
        }

        void Compare(string expected, string actual)
        {
            expected = expected.Trim(' ', '\t', '\r', '\n');
            actual = actual.Trim(' ', '\t', '\r', '\n');
            if (expected.Equals(actual, StringComparison.InvariantCultureIgnoreCase)) { return; }

            String[] expLines = expected.SplitLines();
            String[] actLines = actual.SplitLines();

            String error = String.Empty;
            for (int i = 0; i < Math.Max(expLines.Length, actLines.Length); i++)
            {
                String exp = (i < expLines.Length) ? expLines[i] : "";
                String act = (i < actLines.Length) ? actLines[i] : "";

                CompareLines(exp, act, ref error);
            }

            if (!String.IsNullOrEmpty(error))
            {
                Assert.Fail(error);
            }
        }

        void CompareLines(String exp, String act, ref String error)
        {
            //Assert.AreEqual(exp, act);

            if (!exp.Equals(act, StringComparison.InvariantCultureIgnoreCase))
            {
                int ci = 0;
                while (ci < exp.Length && ci < act.Length && exp[ci] == act[ci]) { ++ci; }
                String errPoint = new String('-', ci);

                String errorDetail = String.Format("Expected: {0}\r\nBut was:  {1}\r\n          {2}^\r\n", exp, act, errPoint);

                Console.Write(errorDetail);
                error += errorDetail;
            }
            else
            {
                // No error
                Console.WriteLine("          " + act);
            }

        }

        #region Implementing ITestCaseData
        public object[]  Arguments
        {
	        get { return new object[] { this };  }
        }

        public string  Description
        {
	        get { return Comment; }
        }

        public Type  ExpectedException
        {
	        get { return null; }
        }

        public string  ExpectedExceptionName
        {
	        get { return null; }
        }

        public string  IgnoreReason
        {
	        get { return null; }
        }

        public bool  Ignored
        {
	        get { return false; }
        }

        public object  Result
        {
	        get { return null; }
        }

        public string TestName
        {
	        get { return _testName; }
        }
        #endregion
    }
}